package com.k.kc.cryptojava;

import com.k.kc.cryptojava.rsa.RSAKeyReader;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;

/**
 * 私钥
 * 公钥
 * 公钥加密数据，然后私钥解密的情况被称为加密和解密；
 * 私钥加密数据，公钥解密一般被称为签名和验证签名。
 * <p>
 * RSA 私钥
 * openssl genpkey -algorithm RSA -out key.pem  -pkeyopt rsa_keygen_bits:2048  或者
 * openssl genrsa -out key.pem 2048
 * <p>
 * RSA 私钥 转 PKCS#8
 * openssl pkcs8 -in key.pem -topk8 -out pkcs8key.pem
 * <p>
 * PKCS#8 转 RSA
 * openssl rsa -in pkcs8key.pem -out pkcs8key_key.pem
 * <p>
 * 公钥
 * openssl rsa -in key.pem -pubout -out pubkey.pem
 * openssl rsa -in key.pem -RSAPublicKey_out -out pubkey.pem
 * <p>
 * AES 128位 密钥
 * openssl genpkey -algorithm RSA -out key.pem -aes-128-cbc -pass pass:hello
 *
 * @author k
 */
public class RSAUtils {
    public static final String KEY_SHA = "SHA";
    public static final String KEY_MD5 = "MD5";
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";


    public static void main(String[] args) throws Exception {
        // openssl 生成密钥对
        // openssl genrsa -out private_key.pem 2048
        // openssl rsa -in private_key.pem -pubout -out public_key.pem

        // pem 转 der
        //openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
        //openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der

        // read der
        String privateKeyFile = "E:\\test\\rsa\\private_key.der";
        String publicKeyFile = "E:\\test\\rsa\\public_key.der";
        PrivateKey privateKey = RSAKeyReader.readPrivateKeyDer(privateKeyFile);
        PublicKey publicKey = RSAKeyReader.readPublicKeyDer(publicKeyFile);

        // read pem
//        String privateKeyFile = "E:\\test\\rsa\\private_key.pem";
//        String publicKeyFile = "E:\\test\\rsa\\public_key.pem";
//        PrivateKey privateKey = RSAKeyReader.readPrivateKeyPem(privateKeyFile);
//        PublicKey publicKey = RSAKeyReader.readPublicKeyPem(publicKeyFile);

        String data = "测试123哈哈哈噢噢噢噢呃呃呃呃啊啊啊啊八八八八顶顶顶顶顶啊啊啊啊啊啊帆帆帆帆帆帆帆帆帆帆水水水水水水水";
        System.out.println("数据：" + data);

        /*************************************************************************************************/
        System.out.println("私钥加密 公钥解密：");
        //私钥加密
        byte[] privateDataBytes = encryptByPrivateKey(data.getBytes(StandardCharsets.UTF_8), privateKey);
        String privateDataBase64 = encryptBASE64(privateDataBytes);
        System.out.println("私钥加密的数据:" + privateDataBase64);
        //公钥解密
        privateDataBytes = decryptBASE64(privateDataBase64);
        byte[] dataBytes = decryptByPublicKey(privateDataBytes, publicKey);
        System.out.println("公钥解密的数据：" + new String(dataBytes, StandardCharsets.UTF_8));
        /*************************************************************************************************/


        /*************************************************************************************************/
        //公钥加密
        System.out.println("公钥加密 私钥解密：");
        byte[] publicDataBytes = encryptByPublicKey(data.getBytes(StandardCharsets.UTF_8), publicKey);
        String publicDataBase64 = encryptBASE64(publicDataBytes);
        System.out.println("公钥加密的数据:" + publicDataBase64);
        //私钥解密
        publicDataBytes = decryptBASE64(publicDataBase64);
        dataBytes = decryptByPrivateKey(publicDataBytes, privateKey);
        System.out.println("私钥解密的数据：" + new String(dataBytes, StandardCharsets.UTF_8));
        /*************************************************************************************************/

        /*************************************************************************************************/
        System.out.println("签名：");
        //签名：私钥加密
        String sign = sign(data.getBytes(StandardCharsets.UTF_8), privateKey);
        System.out.println("私钥签名：" + sign);
        //验证签名：公钥解密
        boolean verify = verify(data.getBytes(StandardCharsets.UTF_8), publicKey, sign);
        System.out.println("公钥验证签名：" + verify);
        /*************************************************************************************************/
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       加密数据
     * @param privateKey 私钥
     * @return result
     * @throws Exception e
     */
    public static String sign(byte[] data, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data);
        return encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param data      加密数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return 校验成功返回true 失败返回false
     * @throws Exception e
     */
    public static boolean verify(byte[] data, PublicKey publicKey, String sign) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(decryptBASE64(sign));
    }

    /**
     * 私钥解密
     *
     * @param data       密文
     * @param privateKey 私钥
     * @return result
     * @throws Exception e
     */
    public static byte[] decryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 用公钥解密
     *
     * @param data      密文
     * @param publicKey 公钥
     * @return result
     * @throws Exception e
     */
    public static byte[] decryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 用公钥加密
     *
     * @param data      明文
     * @param publicKey 公钥
     * @return result
     * @throws Exception e
     */
    public static byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 用私钥加密
     *
     * @param data       明文
     * @param privateKey 私钥
     * @return result
     * @throws Exception e
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    public static byte[] encryptMD5(byte[] data) throws Exception {
        MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
        md5.update(data);
        return md5.digest();
    }

    public static byte[] encryptSHA(byte[] data) throws Exception {
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
        sha.update(data);
        return sha.digest();
    }
}
